package qf.index;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 整理各科室默认的检索词，方法是从总表（pm_ut_aid_info_0120）近6年中获取各规范的科室（复旦或STEM），按出现的次数排序，取前15条，再使用通义咨询（以下医院科室的英文名称，可以归到风湿病科吗？），对回答明确属于的给予保留，最后形成《search_dept_word.txt》，存放在字典文件中。
 * fuDan_病理科_Department Anatomia Pathol_6152
 * fuDan_病理科_Department Clinical Pathol_6497
 * 合计858条，上面是两条样例，数据分为4部分，分别是复旦或STEM、科室名称、检索词、检索词出现的次数，使用时可以把检索词 OR 起来，比如：https://pubmed.ncbi.nlm.nih.gov/?term=Department Anatomia Pathol[AD] OR Department Clinical Pathol[AD]&sort=pubdate&format=pubmed&size=200
 * 意思是按出版时间返回前200条，默认是包含电子优先，如果只要被medline正式收录的，添加参数:https://pubmed.ncbi.nlm.nih.gov/?term=(Department Anatomia Pathol[AD] OR Department Clinical Pathol[AD]) AND medline[SB]&sort=pubdate&format=pubmed&size=200
 * 添加参数是，需要注意，先把原来的检索词用英文括号括起来，再加上" AND medline[SB]"，意思是它们之间是逻辑 AND 关系。
 * 贾新志 2025.02.15
 */

public class SearchWord {


    public static void main(String[] args) throws SQLException {
        String newDataName = "scholar_25_01.";
        String summaryTable = newDataName + "pm_ut_aid_info_0120";
        String jourMergeTable = newDataName + "basic_jour_merge";
        String deptTopJournalTable = newDataName + "dept_top_journal";

//        readTable(summaryTable, "id", "c1_au`,`is_6year");

        String filePath = "F:\\WorkSpaces\\Data\\src\\main\\resources\\dict\\search_dept_word.txt";
//        System.out.println(filePath);
        String savePath = "F:\\WorkSpaces\\Data\\src\\main\\resources\\dict\\dept_top_journal_article.txt";
        FileUtil.appendUtf8String("2025.02.15 各科室的top期刊，基于发表的论文" + System.lineSeparator(), savePath);
        insertDeptTopJournalTable(summaryTable, "id", "is_6year`,`jid`,`c1_au", jourMergeTable, filePath, deptTopJournalTable, savePath);

    }

    public static void insertDeptTopJournalTable(String summaryTable, String autoId, String field, String jourMergeTable, String filePath, String deptTopJournalTable, String savePath) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Map<Integer, String> jidAndJournalMap = getTopJidSet(jourMergeTable, "id", "zky_category`,`nc_nlm_title_abbreviation");
        List<String> list = FileUtil.readLines(filePath, "utf-8");
        Map<String, Set<String>> deptAndSearchWordSetMap = new HashMap<>();
        for (String s : list) {
            String[] split = s.split("_");
            if (split.length == 4) {
                Utils.putStrSetStr(split[0] + "_" + split[1], split[2].toLowerCase().trim(), deptAndSearchWordSetMap);
            }
        }
        int onePageNum = 1000;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + summaryTable);
        Map<String, Map<Integer, Set<Integer>>> deptJourAidSetMap = new HashMap<>();
        Map<Integer, Set<Integer>> jourAndAidSetMap = new HashMap<>();
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + summaryTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            for (Integer startId : tableList) {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + summaryTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startId + " 表名：" + summaryTable + Utils.printMemoryStr() + "\033[0m");
                for (Entity entry : tableData) {
                    Integer year = entry.getInt("is_6year");
                    Integer jid = entry.getInt("jid");
                    Integer aid = entry.getInt(autoId);
                    if (null != year && year > 0 && jidAndJournalMap.containsKey(jid) && null != aid && aid > 0) {
                        String c1Au = Utils.getStrField(entry, "c1_au");
                        List<En.C1Auth> c1List = (c1Au.contains("sort")) ? JSONObject.parseArray(c1Au, En.C1Auth.class) : new ArrayList<>();
                        for (En.C1Auth c1Auth : c1List) {
                            Utils.putIntSetIntMap(jourAndAidSetMap, jid, aid);
                            putDeptJourAidSetMap(c1Auth.getDeptSet(), deptAndSearchWordSetMap, deptJourAidSetMap, aid, jid);
                        }
                    }
                }
            }
        }
        Db.use().execute("truncate table " + deptTopJournalTable);
        for (Map.Entry<String, Map<Integer, Set<Integer>>> entry : deptJourAidSetMap.entrySet()) {
            String dept = entry.getKey();
            Map<Integer, Set<Integer>> jourAidSetMap = entry.getValue();
            int numberMax = 0;
            double ratioMax = Double.MIN_VALUE;
            double ratioMin = Double.MAX_VALUE;
            for (Map.Entry<Integer, Set<Integer>> map : jourAidSetMap.entrySet()) {
                numberMax = Math.max(map.getValue().size(), numberMax);
                Integer jid = map.getKey();
                Set<Integer> allAidSet = jourAndAidSetMap.containsKey(jid) ? jourAndAidSetMap.get(jid) : new HashSet<>();
                if (!allAidSet.isEmpty()) {
                    ratioMax = Math.max((double) map.getValue().size() / allAidSet.size(), ratioMax);
                    ratioMin = Math.min((double) map.getValue().size() / allAidSet.size(), ratioMin);
                }
            }
            Map<Integer, Double> jidAndScoreMap = new HashMap<>();
            for (Map.Entry<Integer, Set<Integer>> map : jourAidSetMap.entrySet()) {
                Integer jid = map.getKey();
                Set<Integer> aidSet = map.getValue();
                Set<Integer> allAidSet = jourAndAidSetMap.containsKey(jid) ? jourAndAidSetMap.get(jid) : new HashSet<>();
                // x=((实际-原始最小)*想要最大+(原始最大-实际)*想要最小)/(原始最大-原始最小)，署名为该科室的期刊的发文量最多的满分为0.5，该科室刊载量占该刊总的发文量的比率最大的满分为0.5，两者得分相加得到该刊最终的得分。
                double numberScore = ((aidSet.size() - 1) * 0.5 + (numberMax - aidSet.size()) * 0.01) / (numberMax - 1);
                double ratioScore = 0.01;
                if (!allAidSet.isEmpty() && ((ratioMax - ratioMin) != 0)) {
                    double ratio = (double) map.getValue().size() / allAidSet.size();
                    ratioScore = ((ratio - ratioMin) * 0.5 + (ratioMax - ratio) * 0.01) / (ratioMax - ratioMin);
                }
                jidAndScoreMap.put(jid, numberScore + ratioScore);
            }
            Map<Integer, Double> first5JournalMap = Utils.mapSortValueLenDouble(jidAndScoreMap, 5);
            for (Map.Entry<Integer, Double> map : first5JournalMap.entrySet()) {
                // 导出时，如果两项得分相加仍不大于0.5则放弃，最多只导出前5本期刊。
                if (map.getValue() > 0.5) {
                    String journal = jidAndJournalMap.getOrDefault(map.getKey(), "");
                    FileUtil.appendUtf8String(dept + "_" + map.getKey() + "_" + journal + "_" + Utils.takeFourDigits(map.getValue()) + System.lineSeparator(), savePath);
                }
            }
            // 以下代码为验证，程序将各详细参数插入表中，供分析使用。
            for (Map.Entry<Integer, Set<Integer>> map : jourAidSetMap.entrySet()) {
                Integer jid = map.getKey();
                Set<Integer> aidSet = map.getValue();
                Set<Integer> allAidSet = jourAndAidSetMap.containsKey(jid) ? jourAndAidSetMap.get(jid) : new HashSet<>();
                double numberScore = ((aidSet.size() - 1) * 0.5 + (numberMax - aidSet.size()) * 0.01) / (numberMax - 1);
                double ratioScore = 0.01;
                if (!allAidSet.isEmpty() && ((ratioMax - ratioMin) != 0)) {
                    double ratio = (double) map.getValue().size() / allAidSet.size();
                    ratioScore = ((ratio - ratioMin) * 0.5 + (ratioMax - ratio) * 0.01) / (ratioMax - ratioMin);
                }
                try {
                    Db.use().insert(Entity.create(deptTopJournalTable)
                            .set("dept", dept)
                            .set("jid", jid)
                            .set("all_aid_list", JSONUtil.toJsonStr(allAidSet))
                            .set("aid_list", JSONUtil.toJsonStr(aidSet))
                            .set("number_max", numberMax)
                            .set("ratio_max", ratioMax)
                            .set("number_score", numberScore)
                            .set("ratio_score", ratioScore)
                            .set("aid_size", aidSet.size())
                            .set("all_aid_list_size", allAidSet.size())
                            .set("result", JSONUtil.toJsonStr(first5JournalMap))
                            .set("all_score", (numberScore + ratioScore))
                            .set("journal", jidAndJournalMap.getOrDefault(jid, ""))
                    );
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void putDeptJourAidSetMap(Set<String> deptSet, Map<String, Set<String>> deptAndSearchWordSetMap, Map<String, Map<Integer, Set<Integer>>> deptJourAidSetMap, int aid, int jid) {
        if (null != deptSet && !deptSet.isEmpty()) {
            Set<String> lDeptSet = new HashSet<>();
            for (String s : deptSet) {
                lDeptSet.add(s.toLowerCase().trim());
            }
            for (Map.Entry<String, Set<String>> entry : deptAndSearchWordSetMap.entrySet()) {
                Set<String> searchWordSet = entry.getValue();
                for (String searchWord : searchWordSet) {
                    if (lDeptSet.contains(searchWord)) {
                        Utils.putStrIntSetInt(deptJourAidSetMap, entry.getKey(), jid, aid);
                    }
                }
            }
        }
    }

    public static Map<Integer, String> getTopJidSet(String jourMergeTable, String autoId, String field) throws SQLException {
        int onePageNum = 1000;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + jourMergeTable);
        Map<Integer, String> jidAndJournalMap = new HashMap<>();
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + jourMergeTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            for (Integer startId : tableList) {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + jourMergeTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                for (Entity entry : tableData) {
                    String zkyCategory = entry.getStr("zky_category");
                    String journal = entry.getStr("nc_nlm_title_abbreviation");
                    Integer jid = entry.getInt(autoId);
                    if (null != zkyCategory && zkyCategory.contains("\"top\":\"是\"") && null != jid && jid > 0 && null != journal && journal.length() > 2) {
                        jidAndJournalMap.put(jid, journal);
                    }
                }
            }
        }
        return jidAndJournalMap;
    }


    public static void putStrEnStrInt(String type, String name, Map<String, Map<String, Integer>> strStrIntMap) {
        if (null != type && type.length() > 1 && null != name && name.length() > 1 && !Utils.isContainChinese(name)) {
            strStrIntMap.compute(type, (k, v) -> {
                if (v == null) {
                    v = new ConcurrentHashMap<>();
                }
                v.compute(name, (key, value) -> value != null ? (value + 1) : 1);
                return v;
            });
        }
    }

    public static void readTable(String summaryTable, String autoId, String field) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        int onePageNum = 1000;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + summaryTable);
        Map<String, Map<String, Integer>> stemDeptInfoSumMap = new HashMap<>();
        Map<String, Map<String, Integer>> fuDanDeptInfoSumMap = new HashMap<>();
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + summaryTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            for (Integer startId : tableList) {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + summaryTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startId + " 表名：" + summaryTable + Utils.printMemoryStr() + "\033[0m");
                for (Entity entry : tableData) {
                    Integer year = entry.getInt("is_6year");
                    if (null != year && year > 0) {
                        String c1Au = Utils.getStrField(entry, "c1_au");
                        List<En.C1Auth> c1List = (c1Au.contains("sort")) ? JSONObject.parseArray(c1Au, En.C1Auth.class) : new ArrayList<>();
                        for (En.C1Auth c1Auth : c1List) {
                            Set<String> stemSet = c1Auth.getStemSet();
                            Set<String> fuDanSet = c1Auth.getFuDanSet();
                            Set<String> deptSet = c1Auth.getDeptSet();
                            for (String info : deptSet) {
                                for (String stem : stemSet) {
                                    putStrEnStrInt(stem, info, stemDeptInfoSumMap);
                                }
                                for (String fuDan : fuDanSet) {
                                    putStrEnStrInt(fuDan, info, fuDanDeptInfoSumMap);
                                }
                            }
                        }
                    }
                }
            }
        }
        String stemFuDanDeptSumTable = "scholar_25_01.stem_fudan_dept_sum";
        Db.use().execute("truncate table " + stemFuDanDeptSumTable);
        insertStemFuDanDeptSumTable(fuDanDeptInfoSumMap, "fuDan", stemFuDanDeptSumTable);
        insertStemFuDanDeptSumTable(stemDeptInfoSumMap, "STEM", stemFuDanDeptSumTable);
    }

    public static void insertStemFuDanDeptSumTable(Map<String, Map<String, Integer>> fuDanDeptInfoSumMap, String type, String stemFuDanDeptSumTable) {
        for (Map.Entry<String, Map<String, Integer>> entry : fuDanDeptInfoSumMap.entrySet()) {
            Map<String, Integer> stringIntegerMap = mapSortValueIntLen(entry.getValue(), 15);
            for (Map.Entry<String, Integer> map : stringIntegerMap.entrySet()) {
                try {
                    Db.use().insert(Entity.create(stemFuDanDeptSumTable).set("stem_fudan", type).set("dept_name", entry.getKey()).set("dept", map.getKey()).set("sum", map.getValue()));
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static Map<String, Integer> mapSortValueIntLen(Map<String, Integer> mapName, Integer len) {
        Map<String, Integer> result = new LinkedHashMap<>();
        mapName.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue()
                        .reversed()).limit(len).forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
        return result;
    }

    /*
      CREATE TABLE `stem_fudan_dept_sum` (
        `id` int(9) NOT NULL AUTO_INCREMENT,
        `stem_fudan` varchar(255) DEFAULT NULL,
        `dept_name` varchar(255) DEFAULT NULL,
        `dept` varchar(255) DEFAULT NULL,
        `sum` int(9) DEFAULT NULL,
        PRIMARY KEY (`id`)
      ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
     */


}
